/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "sg_local.h"


/*
 ==================
 odDict::odDict
 ==================
*/
odDict::odDict (void){

	memset(keyValuesHashTable, 0, sizeof(keyValuesHashTable));
	keyValues = NULL;
}

/*
 ==================
 odDict::~odDict
 ==================
*/
odDict::~odDict (void){

	keyValue_t	*keyValue, *next;

	for (keyValue = keyValues; keyValue; keyValue = next){
		next = keyValue->next;

		if (keyValue->key)
			odMemory->Free(keyValue->key);
		if (keyValue->value)
			odMemory->Free(keyValue->value);

		odMemory->Free(keyValue);
	}
}

/*
 ==================
 odDict::Write
 ==================
*/
void odDict::Write (fileHandle_t f) const {

	keyValue_t	*keyValue;

	if (keyValues == NULL)
		return;

	odFileSystem->Printf(f, "{" LINE_SEPARATOR);

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next)
		odFileSystem->Printf(f, "\"%s\" \"%s\"" LINE_SEPARATOR, keyValue->key, keyValue->value);

	odFileSystem->Printf(f, "}" LINE_SEPARATOR);
}

/*
 ==================
 odDict::Parse
 ==================
*/
bool odDict::Parse (script_t *script){

	token_t		token;
	char		key[MAX_STRING_LENGTH];
	keyValue_t	*keyValue;
	uint		hashKey;

	if (!odParser->ReadToken(script, &token))
		return false;

	if (token.type != TT_PUNCTUATION || Str_Compare(token.string, "{")){
		odParser->ScriptError(script, "expected '{', found '%s' instead", token.string);
		return false;
	}

	while (1){
		if (!odParser->ReadToken(script, &token))
			return false;

		// Check for end of dictionary
		if (token.type == TT_PUNCTUATION && !Str_Compare(token.string, "}"))
			break;

		// Parse the key/value pair
		if (token.type != TT_STRING){
			odParser->ScriptError(script, "expected quoted string, found '%s' instead", token.string);
			return false;
		}

		Str_Copy(key, token.string, sizeof(key));

		if (!odParser->ReadToken(script, &token))
			return false;

		if (token.type != TT_STRING){
			odParser->ScriptError(script, "expected quoted string, found '%s' instead", token.string);
			return false;
		}

		// Fail if the key already exists
		if (FindKeyValue(key)){
			odParser->ScriptWarning(script, "key '%s' already defined", key);
			continue;
		}

		// Allocate a new keyValue
		keyValue = (keyValue_t *)odMemory->Alloc(sizeof(keyValue_t), TAG_SERVER);

		keyValue->key = odMemory->CopyString(key, TAG_SERVER);
		keyValue->value = odMemory->CopyString(token.string, TAG_SERVER);

		// Link the keyValue in
		keyValue->next = keyValues;
		keyValues = keyValue;

		// Add to hash table
		hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

		keyValue->nextHash = keyValuesHashTable[hashKey];
		keyValuesHashTable[hashKey] = keyValue;
	}

	return true;
}

/*
 ==================
 odDict::ListKeyValues
 ==================
*/
void odDict::ListKeyValues (void) const {

	keyValue_t	*keyValue;

	for (keyValue = keyValues; keyValue; keyValue = keyValue->next)
		odSystem->Printf("%-32s %s\n", keyValue->key, keyValue->value);
}

/*
 ==================
 odDict::FindKeyValue
 ==================
*/
const keyValue_t *odDict::FindKeyValue (const char *key) const {

	keyValue_t	*keyValue;
	uint		hashKey;

	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	for (keyValue = keyValuesHashTable[hashKey]; keyValue; keyValue = keyValue->nextHash){
		if (!Str_ICompare(keyValue->key, key))
			return keyValue;
	}

	return NULL;
}

/*
 ==================
 odDict::FindKeyValue
 ==================
*/
keyValue_t *odDict::FindKeyValue (const char *key){

	keyValue_t	*keyValue;
	uint		hashKey;

	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	for (keyValue = keyValuesHashTable[hashKey]; keyValue; keyValue = keyValue->nextHash){
		if (!Str_ICompare(keyValue->key, key))
			return keyValue;
	}

	return NULL;
}

/*
 ==================
 odDict::AddKeyValue
 ==================
*/
void odDict::AddKeyValue (const char *key, const char *value){

	keyValue_t	*keyValue;
	uint		hashKey;

	// Fail if the key already exists
	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	for (keyValue = keyValuesHashTable[hashKey]; keyValue; keyValue = keyValue->nextHash){
		if (!Str_ICompare(keyValue->key, key))
			return;
	}

	// Allocate a new keyValue
	keyValue = (keyValue_t *)odMemory->Alloc(sizeof(keyValue_t), TAG_SERVER);

	keyValue->key = odMemory->CopyString(key, TAG_SERVER);
	keyValue->value = odMemory->CopyString(value, TAG_SERVER);

	// Link the keyValue in
	keyValue->next = keyValues;
	keyValues = keyValue;

	// Add to hash table
	keyValue->nextHash = keyValuesHashTable[hashKey];
	keyValuesHashTable[hashKey] = keyValue;
}

/*
 ==================
 odDict::RemoveKeyValue
 ==================
*/
void odDict::RemoveKeyValue (const char *key){

	keyValue_t	*keyValue, **previous, **previousHash;
	uint		hashKey;

	hashKey = Str_HashKey(key, KEY_VALUES_HASH_SIZE, false);

	previousHash = &keyValuesHashTable[hashKey];
	while (1){
		keyValue = *previousHash;
		if (!keyValue)
			return;

		if (!Str_ICompare(keyValue->key, key)){
			*previousHash = keyValue->nextHash;

			previous = &keyValues;
			while (1){
				keyValue = *previous;
				if (!keyValue)
					break;

				if (!Str_ICompare(keyValue->key, key)){
					*previous = keyValue->next;

					if (keyValue->key)
						odMemory->Free(keyValue->key);
					if (keyValue->value)
						odMemory->Free(keyValue->value);

					odMemory->Free(keyValue);

					break;
				}

				previous = &keyValue->next;
			}

			return;
		}

		previousHash = &keyValue->nextHash;
	}
}

/*
 ==================
 odDict::GetString
 ==================
*/
bool odDict::GetString (const char *key, const char **value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = defaultValue;
		return false;
	}

	*value = keyValue->value;

	return true;
}

/*
 ==================
 odDict::GetBool
 ==================
*/
bool odDict::GetBool (const char *key, bool *value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = Str_ToInteger(defaultValue) != 0;
		return false;
	}

	*value = Str_ToInteger(keyValue->value) != 0;

	return true;
}

/*
 ==================
 odDict::GetInteger
 ==================
*/
bool odDict::GetInteger (const char *key, int *value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = Str_ToInteger(defaultValue);
		return false;
	}

	*value = Str_ToInteger(keyValue->value);

	return true;
}

/*
 ==================
 odDict::GetFloat
 ==================
*/
bool odDict::GetFloat (const char *key, float *value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		*value = Str_ToFloat(defaultValue);
		return false;
	}

	*value = Str_ToFloat(keyValue->value);

	return true;
}

/*
 ==================
 odDict::GetVec2
 ==================
*/
bool odDict::GetVec2 (const char *key, odVec2 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f", &value.x, &value.y);
		return false;
	}

	sscanf(keyValue->value, "%f %f", &value.x, &value.y);

	return true;
}

/*
 ==================
 odDict::GetVec3
 ==================
*/
bool odDict::GetVec3 (const char *key, odVec3 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f", &value.x, &value.y, &value.z);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f", &value.x, &value.y, &value.z);

	return true;
}

/*
 ==================
 odDict::GetVec4
 ==================
*/
bool odDict::GetVec4 (const char *key, odVec4 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);

	return true;
}

/*
 ==================
 odDict::GetVec5
 ==================
*/
bool odDict::GetVec5 (const char *key, odVec5 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f %f", &value.x, &value.y, &value.z, &value.s, &value.t);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f %f", &value.x, &value.y, &value.z, &value.s, &value.t);

	return true;
}

/*
 ==================
 odDict::GetAngles
 ==================
*/
bool odDict::GetAngles (const char *key, odAngles &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Clear();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f", &value.pitch, &value.yaw, &value.roll);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f", &value.pitch, &value.yaw, &value.roll);

	return true;
}

/*
 ==================
 odDict::GetQuat
 ==================
*/
bool odDict::GetQuat (const char *key, odQuat &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Identity();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f", &value.x, &value.y, &value.z, &value.w);

	return true;
}

/*
 ==================
 odDict::GetMat3
 ==================
*/
bool odDict::GetMat3 (const char *key, odMat3 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Identity();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[1][0], &value[1][1], &value[1][2], &value[2][0], &value[2][1], &value[2][2]);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[1][0], &value[1][1], &value[1][2], &value[2][0], &value[2][1], &value[2][2]);

	return true;
}

/*
 ==================
 odDict::GetMat4
 ==================
*/
bool odDict::GetMat4 (const char *key, odMat4 &value, const char *defaultValue) const {

	const keyValue_t	*keyValue;

	value.Identity();

	keyValue = FindKeyValue(key);
	if (!keyValue){
		sscanf(defaultValue, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[0][3], &value[1][0], &value[1][1], &value[1][2], &value[1][3], &value[2][0], &value[2][1], &value[2][2], &value[2][3], &value[3][0], &value[3][1], &value[3][2], &value[3][3]);
		return false;
	}

	sscanf(keyValue->value, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", &value[0][0], &value[0][1], &value[0][2], &value[0][3], &value[1][0], &value[1][1], &value[1][2], &value[1][3], &value[2][0], &value[2][1], &value[2][2], &value[2][3], &value[3][0], &value[3][1], &value[3][2], &value[3][3]);

	return true;
}

/*
 ==================
 odDict::SetString
 ==================
*/
void odDict::SetString (const char *key, const char *value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value);
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value, TAG_SERVER);
}

/*
 ==================
 odDict::SetBool
 ==================
*/
void odDict::SetBool (const char *key, bool value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, Str_FromInteger(value != false));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(Str_FromInteger(value != false), TAG_SERVER);
}

/*
 ==================
 odDict::SetInteger
 ==================
*/
void odDict::SetInteger (const char *key, int value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, Str_FromInteger(value));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(Str_FromInteger(value), TAG_SERVER);
}

/*
 ==================
 odDict::SetFloat
 ==================
*/
void odDict::SetFloat (const char *key, float value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, Str_FromFloat(value));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(Str_FromFloat(value), TAG_SERVER);
}

/*
 ==================
 odDict::SetVec2
 ==================
*/
void odDict::SetVec2 (const char *key, const odVec2 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetVec3
 ==================
*/
void odDict::SetVec3 (const char *key, const odVec3 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetVec4
 ==================
*/
void odDict::SetVec4 (const char *key, const odVec4 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetVec5
 ==================
*/
void odDict::SetVec5 (const char *key, const odVec5 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetAngles
 ==================
*/
void odDict::SetAngles (const char *key, const odAngles &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetQuat
 ==================
*/
void odDict::SetQuat (const char *key, const odQuat &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetMat3
 ==================
*/
void odDict::SetMat3 (const char *key, const odMat3 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}

/*
 ==================
 odDict::SetMat4
 ==================
*/
void odDict::SetMat4 (const char *key, const odMat4 &value){

	keyValue_t	*keyValue;

	keyValue = FindKeyValue(key);
	if (!keyValue){
		AddKeyValue(key, value.ToString(-1));
		return;
	}

	if (keyValue->value)
		odMemory->Free(keyValue->value);

	keyValue->value = odMemory->CopyString(value.ToString(-1), TAG_SERVER);
}
